/*-
 * Copyright (c) 2009 Jonathan Anderson
 * All rights reserved.
 *
 * WARNING: THIS IS EXPERIMENTAL SECURITY SOFTWARE THAT MUST NOT BE RELIED
 * ON IN PRODUCTION SYSTEMS.  IT WILL BREAK YOUR SOFTWARE IN NEW AND
 * UNEXPECTED WAYS.
 * 
 * This software was developed at the University of Cambridge Computer
 * Laboratory with support from a grant from Google, Inc. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/capability.h>

#include <errno.h>
#include <fcntl.h>

#include <iostream>
#include <sstream>

#include <QDir>
#include <QFileInfo>

#include "Cache.h"
#include "DBusClient.h"
#include "FileDialog.h"
#include "OperationCancelledException.h"
#include "UserAngel.h"

#include "cap.h"

using namespace std;
using namespace userAngel;


QString FileDialog::argString() const { return ""; }


void FileDialog::readArguments(Client *client, userAngel::UserAngel::Datum d)
{
	options = d.toPowerbox();
	client->dbus().setUI(options.ui, options.parent_window);
}


void FileDialog::run(Client *client)
{
	QList<File> files = display(client, &options);

	client->ua().send((int32_t) files.size());

	for(int i = 0; i < files.size(); i++)
	{
		const File& file = files.at(i);
		cout << " \"" << file.filename().toStdString() << '"';
		client->addFile(file);

		client->ua().send(file.capName(), QList<int>() << file.fd());
	}
}


QList<File> FileDialog::display(Client *client,
                                struct ua_powerbox_options *options)
{
	const int BASE_FLAG_MASK =
		O_NONBLOCK | O_SHLOCK | O_EXLOCK
		| O_DIRECT | O_NOCTTY | O_TTY_INIT  | O_NOFOLLOW;

	const int READ_FLAG_MASK = BASE_FLAG_MASK | O_RDONLY | O_EXEC;
	const int WRITE_FLAG_MASK =
		BASE_FLAG_MASK
		| O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_EXCL
		| O_FSYNC | O_SYNC;


	const cap_rights_t BASE_CAP_MASK = CAP_SEEK | CAP_FSTAT | CAP_FCNTL;
	const cap_rights_t READ_CAP_MASK = BASE_CAP_MASK | CAP_READ;
	const cap_rights_t WRITE_CAP_MASK =
		BASE_CAP_MASK | CAP_WRITE | CAP_FTRUNCATE | CAP_FSYNC;


	const char *op;
	switch(options->operation)
	{
		case UA_OPEN_FILE:
			op = options->mult ? "Open Files" : "Open File";
			options->flags &= READ_FLAG_MASK;
			options->rights &= READ_CAP_MASK;
			break;

		case UA_SAVE_FILE:
			op = "Save File";
			options->flags &= WRITE_FLAG_MASK;
			options->rights &= WRITE_CAP_MASK;
			break;

		case UA_SELECT_DIR:
			op = "Choose Directory";
			options->flags = 0;
			options->rights = 0;
			break;
	};


	if(options->filterlen > 32)
		throw IllegalArgumentException(
			QObject::tr("Filter length %1 > 32").arg(options->filterlen));

	QString filter(QByteArray(options->filter, options->filterlen));
	QString startPath = client->cwd();
	QString windowTitle = client->name();//options->window_title + client->name();


	// get the filenames from the user
	QStringList tmp;
	switch(options->ui)
	{
		case UA_DUMMY:
		case UA_QT:
		case UA_KDE:
			tmp = client->dbus().openFile(
				options->operation, windowTitle, startPath, filter,
				(bool) options->mult);
			break;

		default:
			fprintf(stderr, "Unhandled powerbox UI type %i\n",
			                options->ui);
	}
	const QStringList filenames = tmp;


	if((filenames.size() == 1) && (filenames.at(0).size() == 0))
		throw OperationCancelledException("DBus Powerbox");



	// open the files using the requested flags and rights
	QList<File> files;
	int len = filenames.size();

	for(int i = 0; i < len; i++)
	{
		QString name(filenames.at(i));
		QFileInfo fullPath(name);

		// update the client's working directory
		if(i == 0) client->setCWD(fullPath.dir().path());

		int fd = cap_open(name,
		                  options->flags, options->rights, options->umask);

		if(fd < 0) continue;

		File file(name, fd, options->rights);

		client->addFile(file);
		files << file;
	}

	return files;
}

